﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// <summary>
    /// 此类用于“查找结果”树型框中表示找到的一个文本片段的相关信息。
    /// 此类的实例总是出现在“FindDocumentTreeViewItem”类的某个实例下级。
    /// </summary>
    public class FindLineTreeViewItem : TreeViewItem, IFindItem
    {
        /// <summary>
        /// [构造方法]用于“查找结果”树型框中表示找到的一个文本片段的相关信息。
        /// </summary>
        /// <param name="fileFullPath">Markdown 文件的完整路径。</param>
        /// <param name="fileShortName">文件短名。</param>
        /// <param name="lineNumber">所在行的序号。</param>
        /// <param name="startLineOffset">起始位置。</param>
        /// <param name="keywordLength">关键词长度。</param>
        /// <param name="lineText">所在行文本。</param>
        /// <param name="foreColor">前景色。</param>
        /// <param name="type">文本类型。为“ Normal, Anchor, TaskListItem, TodoComment”之一。
        /// 不同值会使此类某实例的外观不同。</param>
        /// <param name="textDecorations">文字修饰线。</param>
        public FindLineTreeViewItem(string fileFullPath, string fileShortName,
            int lineNumber, int startLineOffset,
            int keywordLength, string lineText, Brush foreColor, ItemType type,
            TextDecorationCollection textDecorations = null)
        {
            this.Type = type;
            this.Margin = margin;
            this.BorderThickness = new Thickness(1);
            this.SnapsToDevicePixels = true;

            miInsertLink = new MenuItem()
            {
                Header = "引用锚(_L)",
                ToolTip = "此行文本中必须包含一个锚\r\n锚的样式类似：[锚名](@锚ID 锚说明文字)",
                Style = TryFindResource("MetroMenuItem") as Style,
            };
            miInsertLink.Click += MiInsertLink_Click;

            MenuItem miJumpToSource = new MenuItem()
            {
                Header = "跳转到(_J)",
                Style = TryFindResource("MetroMenuItem") as Style,
            };
            miJumpToSource.Click += MiJumpToSource_Click;

            this.ContextMenu = new ContextMenu()
            {
                FontSize = 14,
                Style = Globals.MainWindow.TryFindResource("MetroContextMenu") as Style,
                FontFamily = new FontFamily("Arial,SimSun"),
            };
            this.ContextMenu.Items.Add(miInsertLink);
            this.ContextMenu.Items.Add(miJumpToSource);
            this.ContextMenu.Opened += ContextMenu_Opened;

            if (string.IsNullOrEmpty(fileFullPath))
            {
                this.fileFullPath = fileShortName;
            }
            else
            {
                this.fileFullPath = fileFullPath;
            }
            this.lineNumber = lineNumber;
            this.startLineOffset = startLineOffset;
            this.keywordLength = keywordLength;
            this.ToolTip = this.lineText = lineText;
            if (foreColor != null)
            {
                this.Foreground = foreColor;
            }

            headerTextBlock = new TextBlock()
            {
                Foreground = this.Foreground,
                //FontSize = 14,//加上之后，大纲视图就不能自动调整字号演示了。
                //FontFamily = defFontFamily,
            };

            if (CustomMarkdownSupport.IsCodeBlockLine(this.lineText))
            {
                headerTextBlock.TextDecorations = TextDecorations.Underline;
            }

            var text = this.ShortLineText;

            var headerOfTaskList = "";
            var tailOfTaskList = "";
            if (text.StartsWith("[+]"))
            {
                headerOfTaskList = "[+]";
                tailOfTaskList = text.Substring(3);

                headerTextBlock.Inlines.Add(new Span(new Run(headerOfTaskList))
                {
                    Foreground = (foreColor == null ? Brushes.Black : foreColor),
                });

                headerTextBlock.Inlines.Add(new Span(new Run(" " + tailOfTaskList.TrimStart(new char[] { ' ' })) { Foreground = (foreColor == null ? Brushes.Black : foreColor) }));
            }
            else if (text.StartsWith("[-]") || text.StartsWith("[－]"))
            {
                headerOfTaskList = "[-]";
                tailOfTaskList = text.Substring(3);

                headerTextBlock.Inlines.Add(new Span(new Run(headerOfTaskList))
                {
                    Foreground = Brushes.White,
                    Background = (foreColor == null ? Brushes.Black : foreColor),
                });

                headerTextBlock.Inlines.Add(new Span(new Run(" " + tailOfTaskList.TrimStart(new char[] { ' ' })) { Foreground = (foreColor == null ? Brushes.Black : foreColor) }));
            }
            else if (text.StartsWith("[%]"))
            {
                headerOfTaskList = "[%]";
                tailOfTaskList = text.Substring(3);

                headerTextBlock.Inlines.Add(new Span(new Run(headerOfTaskList))
                {
                    Foreground = Brushes.White,
                    Background = (foreColor == null ? Brushes.Black : foreColor),
                });

                headerTextBlock.Inlines.Add(new Span(new Run(" " + tailOfTaskList.TrimStart(new char[] { ' ' })) { Foreground = (foreColor == null ? Brushes.Black : foreColor) }));
            }
            else if (text.StartsWith("[#]"))
            {
                headerOfTaskList = "[#]";
                tailOfTaskList = text.Substring(3);

                headerTextBlock.Inlines.Add(new Span(new Run(headerOfTaskList))
                {
                    Foreground = (foreColor == null ? Brushes.Black : foreColor),
                });

                headerTextBlock.Inlines.Add(new Span(new Run(" " + tailOfTaskList.TrimStart(new char[] { ' ' })) { Foreground = (foreColor == null ? Brushes.Black : foreColor) }));
            }
            else
            {
                if (CustomMarkdownSupport.IsDateLine(text))
                {
                    var dateTime = CustomMarkdownSupport.GetDateFromDateLine(text);
                    if (dateTime != null && dateTime.HasValue)
                    {
                        var datelineStatuMark = CustomMarkdownSupport.GetDateLineStatusMark(text);
                        Brush statusColor = Brushes.DarkBlue;
                        if (datelineStatuMark.StartsWith("[-]"))
                        {
                            statusColor = Brushes.Red;
                        }
                        else if (datelineStatuMark.StartsWith("[%]"))
                        {
                            statusColor = Brushes.Green;
                        }
                        else if (datelineStatuMark.StartsWith("[+]"))
                        {
                            statusColor = Brushes.Blue;
                        }
                        else if (datelineStatuMark.StartsWith("[#]"))
                        {
                            statusColor = Brushes.Brown;
                        }

                        headerTextBlock.Inlines.Add(
                            new Span(
                                new Run(
                                    "[" + MainWindow.FormatDateTimeText(dateTime.Value) + "]" + datelineStatuMark
                                    )
                                )
                            {
                                Foreground = statusColor,
                                FontWeight = FontWeights.Bold,
                            }
                        );
                        headerTextBlock.Inlines.Add(
                            new Span(
                                new Run(
                                    " " + CustomMarkdownSupport.GetContentTextOfDateLine(text)
                                )
                            )
                            {
                                Foreground = Brushes.Black,
                            }
                        );
                    }
                }
                else
                {
                    string todoCommentMark;
                    string todoCommentTail;
                    if (CustomMarkdownSupport.IsTodoCommentLine(lineText, out todoCommentMark, out todoCommentTail, "TODO"))
                    {
                        headerTextBlock.Inlines.Add(
                            new Span(new Run("；TODO："))
                            {
                                Foreground = foreColor,
                                FontWeight = FontWeights.Bold,
                            }
                        );
                        headerTextBlock.Inlines.Add(
                            new Span(new Run(todoCommentTail))
                            {
                                Foreground = Brushes.Black,
                            }
                        );
                    }
                    else if (CustomMarkdownSupport.IsTodoCommentLine(lineText, out todoCommentMark, out todoCommentTail, "DONE"))
                    {
                        headerTextBlock.Inlines.Add(
                            new Span(new Run("；DONE："))
                            {
                                Foreground = foreColor,
                                FontWeight = FontWeights.Bold,
                            }
                        );
                        headerTextBlock.Inlines.Add(
                            new Span(new Run(todoCommentTail))
                            {
                                Foreground = Brushes.Black,
                            }
                        );
                    }
                    else if (CustomMarkdownSupport.IsTodoCommentLine(lineText, out todoCommentMark, out todoCommentTail, "DOING"))
                    {
                        headerTextBlock.Inlines.Add(
                            new Span(new Run("；DOING："))
                            {
                                Foreground = foreColor,
                                FontWeight = FontWeights.Bold,
                            }
                        );
                        headerTextBlock.Inlines.Add(
                            new Span(new Run(todoCommentTail))
                            {
                                Foreground = Brushes.Black,
                            }
                        );
                    }
                    else
                    {
                        var level = GetHeaderLevel(text);
                        if (level >= 1 && level <= 6)
                        {
                            var line = text.TrimStart(new char[] { ' ', '　', '#' }).TrimEnd(new char[] { '\r', '\n', });
                            switch (level)
                            {
                                case 1: { headerTextBlock.Text = ((type == ItemType.Header) ? "" : "　") + line; headerTextBlock.Foreground = new SolidColorBrush(new Color() { A = 255, R = 255, G = 76, B = 85 }); headerTextBlock.FontWeight = FontWeights.Bold; break; }
                                case 2: { headerTextBlock.Text = ((type == ItemType.Header) ? "" : "　　") + line; headerTextBlock.Foreground = new SolidColorBrush(new Color() { A = 255, R = 255, G = 106, B = 0 }); break; }
                                case 3: { headerTextBlock.Text = ((type == ItemType.Header) ? "" : "　　　") + line; headerTextBlock.Foreground = new SolidColorBrush(new Color() { A = 255, R = 0, G = 143, B = 0 }); break; }
                                case 4: { headerTextBlock.Text = ((type == ItemType.Header) ? "" : "　　　　") + line; headerTextBlock.Foreground = new SolidColorBrush(new Color() { A = 255, R = 0, G = 142, B = 130 }); break; }
                                case 5: { headerTextBlock.Text = ((type == ItemType.Header) ? "" : "　　　　　") + line; headerTextBlock.Foreground = new SolidColorBrush(new Color() { A = 255, R = 7, G = 85, B = 209 }); break; }
                                case 6: { headerTextBlock.Text = ((type == ItemType.Header) ? "" : "　　　　　　") + line; headerTextBlock.Foreground = new SolidColorBrush(new Color() { A = 255, R = 193, G = 0, B = 174 }); break; }
                                default: { headerTextBlock.Text = line; headerTextBlock.Foreground = Brushes.Black; break; }
                            }
                        }
                        else
                        {
                            headerTextBlock.Text = text.Trim(new char[] { ' ', '\t', '　', '\n', '\r', });  //这个首尾的空行还是不要出现的好。
                            headerTextBlock.Foreground = Brushes.Black;
                        }
                    }
                }
            }

            this.foregroundOfText = headerTextBlock.Foreground;
            this.Header = headerTextBlock;
            if (textDecorations != null)
            {
                headerTextBlock.TextDecorations = textDecorations;
            }
            this.PreviewMouseDoubleClick += FindLineTreeViewItem_MouseDoubleClick;
            this.KeyDown += FindLineTreeViewItem_KeyDown;

            //更改默认样式
            var style = FindResource("MetroTreeViewItem") as Style;
            if (style != null) this.Style = style;
            this.Background = Brushes.Transparent;

            this.MouseDown += FindLineTreeViewItem_MouseDown;
        }

        private void FindLineTreeViewItem_MouseDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            this.IsSelected = true;
        }

        private MenuItem miInsertLink;

        private void ContextMenu_Opened(object sender, RoutedEventArgs e)
        {
            if (IsAnchor)
            {
                this.miInsertLink.Visibility = Visibility.Visible;
            }
            else
            {
                this.miInsertLink.Visibility = Visibility.Collapsed;
            }
        }

        /// <summary>
        /// 取标题层级值。
        /// </summary>
        /// <param name="lineText">源文本。</param>
        private static int GetHeaderLevel(string lineText)
        {
            Regex regex = new Regex(@"^[ 　]{0,3}#.*$");
            var match = regex.Match(lineText);
            if (match.Success == false) return 0;

            lineText = lineText.TrimStart(new char[] { ' ', '　' });
            if (lineText.StartsWith("######")) return 6;
            if (lineText.StartsWith("#####")) return 5;
            if (lineText.StartsWith("####")) return 4;
            if (lineText.StartsWith("###")) return 3;
            if (lineText.StartsWith("##")) return 2;
            if (lineText.StartsWith("#")) return 1;
            return 0;
        }

        /// <summary>
        /// 默认字体。//还是应由所在的树型框决定，这样便于大纲视图切换字体。
        /// </summary>
        //private static FontFamily defFontFamily = new FontFamily("SimSun");

        /// <summary>
        /// 按下回车键时，跳转到指向的文本。
        /// </summary>
        private void FindLineTreeViewItem_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                MiJumpToSource_Click(sender, e);
            }
        }

        private static Thickness margin = new Thickness(0, 3, 0, 3);

        /// <summary>
        /// 跳转到当前条目指向的编辑器的相应位置。
        /// </summary>
        private void MiJumpToSource_Click(object sender, RoutedEventArgs e)
        {
            e.Handled = true;
            GotoLine();
        }

        /// <summary>
        /// 跳转到当前条目指向的编辑器的相应位置。
        /// </summary>
        public void GotoLine()
        {
            MarkdownEditor efi = null;
            foreach (var item in Globals.MainWindow.mainTabControl.Items)
            {
                var edit = item as MarkdownEditor;
                if (edit == null) continue;

                if (string.IsNullOrEmpty(edit.FullFilePath))
                {
                    if (edit.ShortFileName == this.FileFullPath)
                    {
                        efi = edit;
                        break;
                    }
                }
                else
                {
                    if (edit.FullFilePath.ToLower() == this.fileFullPath.ToLower())
                    {
                        efi = edit;
                        break;
                    }
                }
            }

            if (efi == null)
            {
                Globals.MainWindow.OpenDocuments(new string[] { this.FileFullPath, });
                foreach (var item in Globals.MainWindow.mainTabControl.Items)
                {
                    var edit = item as MarkdownEditor;
                    if (edit == null) continue;

                    if (string.IsNullOrEmpty(edit.FullFilePath))
                    {
                        if (edit.ShortFileName == this.FileFullPath)
                        {
                            efi = edit;
                            break;
                        }
                    }
                    else
                    {
                        if (edit.FullFilePath.ToLower() == this.fileFullPath.ToLower())
                        {
                            efi = edit;
                            break;
                        }
                    }
                }

                if (efi == null)
                {
                    LMessageBox.Show("意外错误，无法打开对应文档！", Globals.AppName,
                         MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }
            }

            try
            {
                Globals.MainWindow.mainTabControl.SelectedItem = efi;
                var line = efi.EditorBase.Document.GetLineByNumber(this.lineNumber);
                if (line != null)
                {
                    efi.EditorBase.UpdateLayout();

                    var selDest = line.Offset + this.startLineOffset;
                    if (selDest >= 0 && selDest < efi.EditorBase.Document.TextLength)
                    {
                        efi.EditorBase.Select(selDest, this.keywordLength + (Type == ItemType.Anchor ? 2 : 0));
                        efi.EditorBase.ScrollToLine(line.LineNumber);
                    }

                    //还是没解决焦点问题
                    //FocusManager.SetFocusedElement(efi.EditorBase.TextArea, efi.EditorBase.TextArea);
                    efi.UpdateLayout();
                    efi.EditorBase.Focus();
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show("文档内容可能已发生改变，无法定位！\r\n" + ex.Message,
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            TreeView masterTreeView = null;
            var parentElement = this.Parent as FrameworkElement;
            while (parentElement != null)
            {
                var treeVeiw = parentElement as TreeView;
                if (treeVeiw != null)
                {
                    masterTreeView = treeVeiw;
                    break;
                }
                else
                {
                    parentElement = parentElement.Parent as FrameworkElement;
                }
            }

            if (masterTreeView != null && masterTreeView.Tag != null)
            {
                var window = masterTreeView.Tag as GotoWindow;
                if (window != null) window.Close();
            }
        }

        /// <summary>
        /// 表示“查找结果”框中的结果条目指向的文本是哪种类型。
        /// 不同类型会呈现出不同的外观、行为。
        /// </summary>
        public enum ItemType
        {
            Normal,             //普通文本
            Anchor,             //锚
            TaskListItem,       //任务列表项
            TodoComment,        //待完成任务注释
            Header,             //标题文本
        }

        /// <summary>
        /// 此条目表示找到的是哪种类型的文本。
        /// </summary>
        public ItemType Type { get; set; }

        /// <summary>
        /// 如果当前条目是个锚，则点击此菜单项向当前活动文档添加对这个锚的引用。
        /// * 锚不需要与当前活动文档在同一 Markdown 文件中。
        /// </summary>
        private void MiInsertLink_Click(object sender, RoutedEventArgs e)
        {
            //在当前文档中插入对此锚的引用
            if (IsAnchor == false || string.IsNullOrEmpty(anchorID))
            {
                LMessageBox.Show("    这行文本中不包含“锚”，无法引用！" +
                    "“锚”的样式类似：\r\n    [锚名](@锚ID 锚文明文本)",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            //如果找到的锚在代码块中，提示禁止
            if (CustomMarkdownSupport.IsCodeBlockLine(this.LineText))
            {
                LMessageBox.Show("    这个“锚”是在一个代码块行中，在 Html 中不起作用，无法引用！",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var editor = Globals.MainWindow.ActivedEditor;
            if (editor == null)
            {
                LMessageBox.Show("当前没有打开任何文档，无法添加引用！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var destFilePath = editor.FullFilePath;

            var srcPath = this.FileFullPath.ToLower();

            //思路：先找出共同的、最接近的祖先级目录
            var separator = new char[] { '\\', '/' };
            string[] src = srcPath.Split(separator, StringSplitOptions.RemoveEmptyEntries);
            string[] dest = destFilePath.ToLower().Split(separator, StringSplitOptions.RemoveEmptyEntries);

            var minIndex = Math.Min(src.Length, dest.Length) - 1;

            if (minIndex < 0) return;

            int aIndex = -1;
            for (int i = 0; i <= minIndex; i++)
            {
                if (src[i] != dest[i])
                {
                    aIndex = i;
                    break;
                }
            }

            //生成相对路径的前半部分，类似（../../）这样子。
            string[] file = destFilePath.Split(separator, StringSplitOptions.RemoveEmptyEntries);
            string header;
            if (srcPath == destFilePath || aIndex < 0)//同一文件内部引用
            {
                header = "";
            }
            else
            {
                header = BuildHtmlRefText(file.Length - aIndex - 1);

                for (int i = aIndex; i < src.Length; i++)
                {
                    header += src[i];
                    header += "/";
                }

                header = header.Replace(".md", ".html");
                if (header.EndsWith(".html/"))
                {
                    header = header.Substring(0, header.Length - 1);
                }
            }

            var headerTextOfAnchor = anchorDescription;
            if (string.IsNullOrWhiteSpace(headerTextOfAnchor))
                headerTextOfAnchor = anchorName;

            if (string.IsNullOrWhiteSpace(headerTextOfAnchor))
            {
                Regex regSquareStart = new Regex(@"(?<=^[\{\[])[^\[\]\(（]*");
                var matchSquareStart = regSquareStart.Match(this.LineText);
                if (matchSquareStart.Success)
                {
                    headerTextOfAnchor = matchSquareStart.Value.Trim(new char[] { ' ', '　', '\t', });
                }
                else
                    headerTextOfAnchor = anchorID;
            }

            var oldSelStart = editor.EditorBase.SelectionStart;
            editor.EditorBase.BeginChange();

            var linkText = "[" + headerTextOfAnchor + "](" + header + "#" + anchorID + ")";
            editor.EditorBase.Document.Replace(editor.EditorBase.SelectionStart, editor.EditorBase.SelectionLength, linkText);

            editor.EditorBase.EndChange();

            var destSel = oldSelStart + 1;
            if (destSel < editor.EditorBase.Document.TextLength)
            {
                editor.EditorBase.Select(destSel, headerTextOfAnchor.Length);
            }
        }

        /// <summary>
        /// 生成 Html 引用文本的前缀。
        /// </summary>
        /// <param name="layersCount">路径层级的差值。用以生成路径前缀。</param>
        public static string BuildHtmlRefText(int layersCount)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < layersCount; i++)
            {
                sb.Append("../");
            }
            return sb.ToString();
        }

        /// <summary>
        /// 锚名称。
        /// </summary>
        private string anchorName;

        /// <summary>
        /// 锚的说明文本。
        /// </summary>
        private string anchorDescription;

        /// <summary>
        /// 锚 ID。
        /// </summary>
        private string anchorID;

        /// <summary>
        /// 当前条目指向的文本是个锚。
        /// </summary>
        public bool IsAnchor
        {
            get
            {
                // 先考虑简写的锚
                var regSimpleAnchor = new Regex(@"(?<=[\(（][ 　\t]*?[@][ 　\t]*?)[a-zA-Z][a-zA-Z0-9\-_]*?(?=[ 　\t]*?[\)）])");
                var matchSimpleAnchor = regSimpleAnchor.Match(this.LineText);
                if (matchSimpleAnchor.Success)
                {
                    anchorID = matchSimpleAnchor.Value;
                    return true;
                }

                // 再考虑传统的锚
                if (string.IsNullOrEmpty(this.lineText)) return false;

                var text = this.lineText.Replace("（", ")").Replace("）", ")");

                var index = text.IndexOf("](@");
                if (index < 0) return false;

                var startIndex = text.LastIndexOf('[', index - 1);
                if (startIndex < 0) return false;

                var endIndex = text.IndexOf(')', index + 3);
                if (endIndex < 0) return false;

                this.anchorName = text.Substring(startIndex + 1, index - startIndex - 1);
                this.anchorID = text.Substring(index + 3, endIndex - index - 3);

                Regex reg = new Regex(@"[ 　\t]{1,}");
                var match = reg.Match(this.anchorID);
                if (match.Success && match.Index > 0)
                {
                    this.anchorDescription = this.anchorID.Substring(match.Index + match.Length);
                    this.anchorID = this.anchorID.Substring(0, match.Index);
                }

                if (string.IsNullOrEmpty(anchorID)) return false;

                return true;
            }
        }

        /// <summary>
        /// 双击跳转到指向的编辑器的对应位置。
        /// </summary>
        void FindLineTreeViewItem_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            e.Handled = true;
            //MiInsertLink_Click(sender, e);//添加“锚”的使用频率远远低于跳转到目标文本
            MiJumpToSource_Click(sender, e);
        }

        /// <summary>
        /// 行文本的缩略形式。多于50个字符的话，后面会以...的形式呈现。
        /// </summary>
        private string ShortLineText
        {
            get
            {
                if (string.IsNullOrEmpty(this.lineText)) return "";
                if (this.lineText.Length <= 50) return this.lineText;

                return lineText.Substring(0, 50) + "...";
            }
        }

        private string fileFullPath;
        /// <summary>
        /// 找到的文本所在文件的完整路径。
        /// </summary>
        public string FileFullPath
        {
            get { return fileFullPath; }
            set { fileFullPath = value; }
        }

        private int lineNumber = -1;
        /// <summary>
        /// 找到的文本所在行的行号。
        /// </summary>
        public int LineNumber
        {
            get { return lineNumber; }
            set { lineNumber = value; }
        }

        private int startLineOffset = -1;
        /// <summary>
        /// 找到的文本的起始索引。
        /// </summary>
        public int StartLineOffset
        {
            get { return startLineOffset; }
            set { startLineOffset = value; }
        }

        private string lineText;
        /// <summary>
        /// 找到的文本所在行的文本。
        /// </summary>
        public string LineText
        {
            get { return lineText; }
            set { lineText = value; }
        }

        private int keywordLength;
        /// <summary>
        /// 关键词长度，这个用于决定跳转到指向的文本位置后应选取多少个字符。
        /// </summary>
        public int KeywordLength
        {
            get { return keywordLength; }
            set { keywordLength = value; }
        }

        /// <summary>
        /// 所指向的文件的短名称。
        /// </summary>
        public string ShortName
        {
            get
            {
                if (System.IO.File.Exists(this.fileFullPath) == false) return "文件不存在或未存盘！";

                var fileInfo = new System.IO.FileInfo(this.fileFullPath);
                return fileInfo.Name;
            }
        }

        private TextBlock headerTextBlock;
        /// <summary>
        /// 标头文本块。
        /// </summary>
        public TextBlock HeaderTextBlock
        {
            get { return this.headerTextBlock; }
        }

        private Brush foregroundOfText = Brushes.Black;
        /// <summary>
        /// 文本前景色，用于树型框失焦时保留边框。
        /// </summary>
        public Brush ForegroundOfText { get { return foregroundOfText; } }
    }
}
